/*
 * 础光实时操作系统PhotonRTOS -- 中断服务例程
 *
 * Copyright (C) 2022 国科础石(重庆)软件有限公司
 *
 * 作者: Yili Zhang <s-zhangyili@kernelsoft.com>
 *
 * License terms: GNU General Public License (GPL) version 3
 *
 */

#include <autosar/internal.h>

VAR(struct accurate_counter, AUTOMATIC) osek_all_interrupt_nesting[MAX_CPUS];
VAR(uintptr_t, AUTOMATIC) osek_all_interrupt_flags[MAX_CPUS];
VAR(struct accurate_counter, AUTOMATIC) osek_os_interrupt_nesting[MAX_CPUS];
VAR(uintptr_t, AUTOMATIC) osek_os_interrupt_flags[MAX_CPUS];

/**
 * 恢复由DisableAllInterrupts保存的状态。
 * 语法：
 *     void EnableAllInterrupts ( void )
 * 参数（输入）：
 *     无
 * 参数（输出）：
 *     无
 * 特殊说明：
 *     该服务可以在一类或者二类ISR、任务级中调用，
 *     但是不能从钩子例程中调用。
 *     该服务是DisableAllInterrupts的相对服务，
 *     DisableAllInterrupts服务必须先调用，
 *     其目的是实现代码临界区。
 *     在该临界区中，不允许API服务调用。
 *     实现应当适配这个服务到目标硬件，并提供最小负载。
 *     通常，此服务可以让中央处理单元处理中断。
 * 状态：
 *     标准：
 *        无
 *     扩展：
 *        无
 * 一致性：
 *     BCC1, BCC2, ECC1, ECC2
 */
FUNC(void, OS_CODE) EnableAllInterrupts(void)
{
	/**
	 * 实际上，应当严格禁止调用此服务
	 * 使用SuspendAllInterrupt/ResumeAllInterrupts来替代
	 */
	enable_irq();
}

/**
 * 禁止所有硬件所支持的中断。
 * 在此之前的状态被保存以用于EnableAllInterrupts调用。
 * 语法：
 *     void DisableAllInterrupts ( void )
 * 参数（输入）：
 *     无
 * 参数（输出）：
 *     无
 * 特殊说明：
 *     该服务可以在一类或者二类ISR、任务级中调用，
 *     但是不能从钩子例程中调用。
 *     该服务目的是开始代码临界区。
 *     该服务应该通过调用EnableAllInterrupts来结束。
 *     在该临界区中，不允许API服务调用。
 *     实现应当适配这个服务到目标硬件，并提供最小负载。
 *     通常，此服务可以让中央处理单元禁止识别中断。
 *     注意：该服务不支持嵌套。
 *     如果临界区中的嵌套是必要的，
 *     使用库SuspendOSInterrupts/ResumeOSInterrupts
 *     或者SuspendAllInterrupt/ResumeAllInterrupts
 * 状态：
 *     标准：
 *        无
 *     扩展：
 *         无
 * 一致性：
 *     BCC1, BCC2, ECC1, ECC2
 */
FUNC(void, OS_CODE) DisableAllInterrupts(void)
{
	/**
	 * 实际上，应当严格禁止调用此服务
	 * 使用SuspendAllInterrupt/ResumeAllInterrupts来替代
	 */
	disable_irq();
}

/**
 * 保存所有中断状态，并禁止硬件支持的所有中断。
 * 语法：
 *     void SuspendAllInterrupts( void )
 * 参数（输入）：
 *     无
 * 参数（输出）：
 *     无
 * 特殊说明：
 *     该服务可以在一类或者二类ISR、
 *     警报回调、任务级中调用
 *     但是不能从所有钩子例程中调用。
 *     该服务的目的是保存所有类型的
 *     中断相关的临界区代码。
 *     应当通过调用ResumeAllInterrupts服务来结束。
 *     除了 SuspendAllInterrupts/ResumeAllInterrupts 对和
 *     SuspendOSInterrupts/ResumeOSInterrupts 对之外，
 *     不允许在此临界区中调用任何 API 服务。
 *     实现应当适配这个服务到目标硬件，并提供最小负载。
 * 状态：
 * 标准：
 *     无
 * 扩展：
 *     无
 * 一致性：
 *     BCC1, BCC2, ECC1, ECC2
 */
FUNC(void, OS_CODE) SuspendAllInterrupts(void)
{
	VAR(CoreIdType, AUTOMATIC) core_id = GetCoreID();
	/**
	 * 如果osek_all_interrupt_nesting<0，说明调用未配对
	 * 这里应该警告
	 */
	if (accurate_read(&osek_all_interrupt_nesting[core_id]) <= 0) {
		accurate_set(&osek_all_interrupt_nesting[core_id], 0);
		local_irq_save(osek_all_interrupt_flags[core_id]);
	}
	accurate_inc(&osek_all_interrupt_nesting[core_id]);
}

/**
 * 恢复中断状态，这些状态由SuspendAllInterrupts服务所保存。
 * 语法：
 *     void ResumeAllInterrupts( void )
 * 参数（输入）：
 *     无
 * 参数（输出）：
 *     无
 * 特殊说明：
 *     该服务可以在一类或者二类ISR、
 *     警报回调、任务级中调用，
 *     但是不能从所有钩子例程中调用。
 *     该服务是SuspendAllInterrupts服务的相对服务。
 *     在这之前调用此服务，其目的是结束临界区代码。
 *     除了 SuspendAllInterrupts/ResumeAllInterrupts 对和
 *          SuspendOSInterrupts/ResumeOSInterrupts 对之外，
 *     不允许在此临界区中调用任何 API 服务。
 *     实现应当适配这个服务到目标硬件，并提供最小负载。
 *     SuspendAllInterrupts/ResumeAllInterrupts可以嵌套。
 *     在嵌套调用SuspendAllInterrupts/ResumeAllInterrupts情况下，
 *     第一个调用SuspendAllInterrupts保存的中断状态会被
 *     最后一个调用ResumeAllInterrupts服务来恢复。
 * 状态：
 *     标准：
 *        无
 *     扩展：
 *        无
 * 一致性：
 *     BCC1, BCC2, ECC1, ECC2
 */
FUNC(void, OS_CODE) ResumeAllInterrupts(void)
{
	VAR(CoreIdType, AUTOMATIC) core_id = GetCoreID();
	accurate_dec(&osek_all_interrupt_nesting[core_id]);
	/**
	 * 如果osek_all_interrupt_nesting<0，说明调用未配对
	 * 这里应该警告
	 */
	if (accurate_read(&osek_all_interrupt_nesting[core_id]) == 0) {
		local_irq_restore(osek_all_interrupt_flags[core_id]);
		preempt_check_resched();
	}
}

/**
 * 保存二类中断的状态，并禁止这些中断。
 * 语法：
 *     void SuspendOSInterrupts( void )
 * 参数（输入）：
 *     无
 * 参数（输出）：
 *     无
 * 特殊说明：
 *     该服务可以在ISR、任务级中调用，
 *     但是不能从钩子例程中调用。
 *     该服务的目的是保护代码临界区。
 *     该临界区应当通过调用ResumeOSInterrupts服务来结束。
 *     除了SuspendAllInterrupts/ResumeAllInterrupts和
 *     SuspendOSInterrupts/ResumeOSInterrupts外，
 *     在临界区内不允许调用其他API服务。
 *     实现应当适配本服务到目标硬件，并提供最小负载。
 *     它仅仅试图禁止所有二类中断。
 *     不过，这不是有效的禁止更多中断的方式。
 * 状态：
 *     标准：
 *        无
 *     扩展：
 *        无
 * 一致性：
 *     BCC1, BCC2, ECC1, ECC2
 */
FUNC(void, OS_CODE) SuspendOSInterrupts(void)
{
	VAR(CoreIdType, AUTOMATIC) core_id = GetCoreID();
	/**
	 * 如果osek_all_interrupt_nesting<0，说明调用未配对
	 * 这里应该警告
	 */
	if (accurate_read(&osek_os_interrupt_nesting[core_id]) <= 0) {
		accurate_set(&osek_os_interrupt_nesting[core_id], 0);
		local_isr_save(osek_os_interrupt_flags[core_id]);
	}
	accurate_inc(&osek_os_interrupt_nesting[core_id]);
}

/**
 * 恢复由SuspendOSInterrupts服务保存的中断状态。
 * 语法：
 *     void ResumeOSInterrupts ( void )
 * 参数（输入）：
 *     无
 * 参数（输出）：
 *     无
 * 特殊说明：
 *     该服务可以在一类或者二类ISR、任务级中调用，
 *     但是不能从钩子例程中调用。
 *     该服务是SuspendOSInterrupts服务的相对服务，
 *     SuspendOSInterrupts服务必须在之前调用，
 *     其目的是结束代码临界区。
 *     除了 SuspendAllInterrupts/ResumeAllInterrupts 对和
 *     SuspendOSInterrupts/ResumeOSInterrupts 对之外，
 *     不允许在此临界区中调用任何 API 服务。
 *     SuspendOSInterrupts/ResumeOSInterrupts可以嵌套。
 *     在嵌套调用SuspendOSInterrupts 和ResumeOSInterrupts的情况下，
 *     由第一个SuspendOSInterrupts 保存的中断状态将被
 *     最后一个ResumeOSInterrupts恢复。
 * 状态：
 *     标准：
 *        无
 *     扩展：
 *        无
 * 一致性：
 *     BCC1, BCC2, ECC1, ECC2
 */
FUNC(void, OS_CODE) ResumeOSInterrupts(void)
{
	VAR(CoreIdType, AUTOMATIC) core_id = GetCoreID();
	accurate_dec(&osek_os_interrupt_nesting[core_id]);
	/**
	 * 如果osek_all_interrupt_nesting<0，说明调用未配对
	 * 这里应该警告
	 */
	if (accurate_read(&osek_os_interrupt_nesting[core_id]) == 0) {
		local_isr_restore(osek_os_interrupt_flags[core_id]);

		preempt_schedule();
	}
}

/**
 * 服务名字
 *      EnableInterruptSource
 * 语法
 *  	StatusType EnableInterruptSource (   ISRType ISRID,   boolean ClearPending )
 * 服务 ID [hex]
 *  	0x31
 * 同步/异步
 *  	同步（Synchronous）
 * 可重入性
 * 	可重入
 * 参数 (in)
 *      ISRID    2 类 ISR 的 ID。
 *      ClearPending    定义挂起标志是否应被清除 (TRUE) 或不 (FALSE)。
 * 参数 (inout)
 * 	None
 * 参数 (out)
 *  None
 * 返回值
 *      StatusType
 * 	    E_OK 无错误。
 *      E_OS_ID ISRID 不是有效的第 2 类 ISR 标识符（扩展状态）
 *      E_OS_CALLEVEL API 函数调用上下文错误（扩展状态）
 *      E_OS_ACCESS 调用应用程序不是 ISRID（服务保护）传入的 ISR 的所有者
 * 描述
 * 	通过修改中断控制器寄存器启用中断源。此外，它可能会清除中断挂起标志
 * 引用
 *  	Os.h
 *      AUTOSAR OS 8.4.35 EnableInterruptSource [SWS_Os_91020]
 */
FUNC(StatusType, OS_CODE) EnableInterruptSource(
	VAR(ISRType, AUTOMATIC) ISRID,
	VAR(boolean, AUTOMATIC) ClearPending)
{
	VAR(StatusType, AUTOMATIC) ret = E_OK;
	P2VAR(struct irq_desc, AUTOMATIC, OS_APPL_DATA) desc = get_irq_desc(ISRID);

#if defined(AUTOSAR_OS_APPLICATION)
	VAR(ApplicationType, AUTOMATIC) app_id;
	P2VAR(struct process_desc, AUTOMATIC, OS_APPL_DATA) pro_desc = current_proc_info();
#endif /* defined(AUTOSAR_OS_APPLICATION) */

	CONTEXT_CHECK(ENV_TASK | ENV_CAT2_ISR)

#if defined(EXTENDED_STATUS)
	/*[SWS_Os_00808]若不是有效的二类中断ID，返回E_OS_ID
	*当前构想中，二类中断属于外设中断，中断号应在16以上
	*/
	if (desc->isr2 != 1) {
		ret = E_OS_ID;
		goto out;
	}
	/**
	 * 函数调用上下文错误，返回E_OS_CALLEVEL
	 * 当前接口只允许在TASK或者ISR2中进行调用
	 * （目前所有中断都是二类中断）
	*/
	if (!in_osek_task() && !in_interrupt()) {
		ret = E_OS_CALLEVEL;
		goto out;
	}
#if defined(AUTOSAR_OS_APPLICATION)
	/**
	 * E_OS_ACCESS 调用应用程序不是 ISRID（服务保护）传入的 ISR 的所有者
	*/
	app_id = GetApplicationID();
	if (app_id != pro_desc->task->app_id) {
		ret = E_OS_ACCESS;
		goto out;
	}
#endif /* defined(AUTOSAR_OS_APPLICATION) */
	/**
	 * [SWS_Os_00809]中断源控制API的嵌套调用，操作系统应返回E_OS_NOFUNC
	 * 防止为已禁用的中断源调用 DisableInterruptSource
	 * 或者为已启用的中断源调用 EnableInterruptSource
	*/
	if (get_irq_status(desc->hw_irq)) {
		ret = E_OS_NOFUNC;
		goto out;
	}
#endif

	unmask_irq(desc->hw_irq);
	/*[SWS_Os_00811]清除中断挂起标志，使得软件可以触发中断
	 *[SWS_Os_00814]清除挂起的中断仅应限于清除中断控制器的挂起标
	 * 志
	 */
	if (ClearPending) {
		ClearPendingInterrupt(ISRID);
	}
out:
	/**
	 * [SWS_Os_00810]中断源控制API错误处理
	 * 如果操作系统在执行DisableInterruptSource时检测到错误，
	 * EnableInterruptSource和ClearPendingInterrupt应返回适当的
	 * StatusType并调用错误钩子，否则应返回E_OK。
	*/
	if (ret != E_OK) {
		os_app_error_hooks(ret);
	}
	return ret;
}

/**
 * 服务名字
 *      DisableInterruptSource
 * 语法
 *  	StatusType DisableInterruptSource ( ISRType ISRID )
 * 服务 ID [hex]
 *  	0x30
 * 同步/异步
 *  	同步（Synchronous）
 * 可重入性
 * 	可重入
 * 参数 (in)
 *      ISRID    2 类 ISR 的 ID。
 * 参数 (inout)
 * 	None
 * 参数 (out)
 *  None
 * 返回值
 *      StatusType
 * 	    E_OK 无错误。
 *      E_OS_ID ISRID 不是有效的第 2 类 ISR 标识符（扩展状态）
 *      E_OS_CALLEVEL API 函数调用上下文错误（扩展状态）
 *      E_OS_ACCESS 调用应用程序不是 ISRID（服务保护）传入的 ISR 的所有者
 * 描述
 * 	通过修改中断控制器寄存器来禁用中断源。
 * 引用
 *  	Os.h
 *      AUTOSAR OS 8.4.36 disableInterruptSource [SWS_Os_91019]
 */
FUNC(StatusType, OS_CODE) DisableInterruptSource(
	VAR(ISRType, AUTOMATIC) ISRID)
{
	VAR(StatusType, AUTOMATIC) ret = E_OK;
	P2VAR(struct irq_desc, AUTOMATIC, OS_APPL_DATA) desc = get_irq_desc(ISRID);

#if defined(AUTOSAR_OS_APPLICATION)
	VAR(ApplicationType, AUTOMATIC) app_id;
	P2VAR(struct process_desc, AUTOMATIC, OS_APPL_DATA) pro_desc = current_proc_info();
#endif

	CONTEXT_CHECK(ENV_TASK | ENV_CAT2_ISR)

#if defined(EXTENDED_STATUS)
	/*[SWS_Os_00808]若不是有效的二类中断ID，返回E_OS_ID
	*当前构想中，二类中断属于外设中断，中断号应在16以上
	*/
	if (desc->isr2 != 1) {
		ret = E_OS_ID;
		goto out;
	}
	/**
	 * 函数调用上下文错误，返回E_OS_CALLEVEL
	 * 当前接口只允许在TASK或者ISR2中进行调用
	 * （目前所有中断都是二类中断）
	*/
	if (!in_osek_task() && !in_interrupt()) {
		ret = E_OS_CALLEVEL;
		goto out;
	}
#if defined(AUTOSAR_OS_APPLICATION)
	/**
	 * E_OS_ACCESS 调用应用程序不是 ISRID（服务保护）传入的 ISR 的所有者
	*/
	app_id = GetApplicationID();
	if (app_id != pro_desc->task->app_id) {
		ret = E_OS_ACCESS;
		goto out;
	}
#endif /* defined(AUTOSAR_OS_APPLICATION) */
	/**
	 * [SWS_Os_00809]中断源控制API的嵌套调用，操作系统应返回E_OS_NOFUNC
	 * 防止为已禁用的中断源调用 DisableInterruptSource
	 * 或者为已启用的中断源调用 EnableInterruptSource
	*/
	if (!get_irq_status(desc->hw_irq)) {
		ret = E_OS_NOFUNC;
		goto out;
	}
#endif

	/*文档要求使用中断控制器禁止中断*/
	mask_irq(desc->hw_irq);

out:
	/**
	 * [SWS_Os_00810]中断源控制API错误处理
	 * 如果操作系统在执行DisableInterruptSource时检测到错误，
	 * EnableInterruptSource和ClearPendingInterrupt应返回适当的
	 * StatusType并调用错误钩子，否则应返回E_OK。
	*/
	if (ret != E_OK) {
		os_app_error_hooks(ret);
	}
	return ret;
}

/**
 * 服务名字
 *      ClearPendingInterrupt
 * 语法
 *  	StatusType ClearPendingInterrupt ( ISRType ISRID )
 * 服务 ID [hex]
 *  	0x32
 * 同步/异步
 *  	同步（Synchronous）
 * 可重入性
 * 	可重入
 * 参数 (in)
 *      ISRID    2 类 ISR 的 ID。
 * 参数 (inout)
 * 	None
 * 参数 (out)
 *  None
 * 返回值
 *      StatusType
 * 	    E_OK 无错误。
 *      E_OS_ID ISRID 不是有效的第 2 类 ISR 标识符（扩展状态）
 *      E_OS_CALLEVEL API 函数调用上下文错误（扩展状态）
 *      E_OS_ACCESS 调用应用程序不是 ISRID（服务保护）传入的 ISR 的所有者
 * 描述
 * 	通过修改中断控制器寄存器清除中断挂起标志。
 * 引用
 *  	Os.h
 *      AUTOSAR OS 8.4.37 ClearPendingInterrupt [SWS_Os_91021]
 */
FUNC(StatusType, OS_CODE) ClearPendingInterrupt(
	VAR(ISRType, AUTOMATIC) ISRID)
{
	VAR(StatusType, AUTOMATIC) ret = E_OK;
	P2VAR(struct irq_desc, AUTOMATIC, OS_APPL_DATA) desc = get_irq_desc(ISRID);

#if defined(AUTOSAR_OS_APPLICATION)
	VAR(ApplicationType, AUTOMATIC) app_id;
	P2VAR(struct process_desc, AUTOMATIC, OS_APPL_DATA) pro_desc = current_proc_info();
#endif /* defined(AUTOSAR_OS_APPLICATION) */

	CONTEXT_CHECK(ENV_TASK | ENV_CAT2_ISR)

#if defined(EXTENDED_STATUS)
	/*[SWS_Os_00808]若不是有效的二类中断ID，返回E_OS_ID
	*当前构想中，二类中断属于外设中断，中断号应在16以上
	*/
	if (desc->isr2 != 1) {
		ret = E_OS_ID;
		goto out;
	}
	/**
	 * 函数调用上下文错误，返回E_OS_CALLEVEL
	 * 当前接口只允许在TASK或者ISR2中进行调用
	 * （目前所有中断都是二类中断）
	*/
	if (!in_osek_task() && !in_interrupt()) {
		ret = E_OS_CALLEVEL;
		goto out;
	}
#if defined(AUTOSAR_OS_APPLICATION)
	/**
	 * E_OS_ACCESS 调用应用程序不是 ISRID（服务保护）传入的 ISR 的所有者
	*/
	app_id = GetApplicationID();
	if (app_id != pro_desc->task->app_id) {
		ret = E_OS_ACCESS;
		goto out;
	}
#endif /* defined(AUTOSAR_OS_APPLICATION) */
#endif

	clear_irq_pending(desc->hw_irq);
out:
	return ret;
}


/**
 * FIXME：由于当前函数必须在二类中断上下文中才会返回正确结果，
 * 那么运行起二类中断的回调函数时已经进入了某个核，因此不用在
 * 判断是在哪个核中
*/
FUNC(ISRType, OS_CODE) GetISRID(void)
{
	VAR(ISRType, AUTOMATIC) irqnr;
	VAR(ISRType, AUTOMATIC) ret = INVALID_ISR;
	P2VAR(struct irq_desc, AUTOMATIC, OS_APPL_DATA) desc;
	/*[SWS_Os_00515],GetISRID()适用于所有可扩展性类*/

	CONTEXT_CHECK(ENV_TASK | ENV_CAT2_ISR | ENV_ERROR_HOOK | ENV_PROTECTION_HOOK)

#if defined(EXTENDED_STATUS)
	/**
	 * [SWS_Os_00264]: 如果它的调用者不是2类ISR，应返回INVALID_ISR。
	*/
	if (!in_interrupt()) {
		goto out;
	}
	/**
	 * [SWS_Os_00263] 若从二类中断中调用，获取ISR的标识符
	 * 二类中断不能嵌套，因此只会进入一次
	 */
	irqnr = current_proc_info()->current_irq;  /*虚拟中断号*/
	desc = get_irq_desc(irqnr);
	if (desc->isr2 != 1) {/*如果不是二类中断，返回INVALID_ISR*/
		goto out;
	} else {
		ret = irqnr;
	}
#endif

out:
	return ret;
}

/**
 * 恢复中断，如果没有正常恢复
 */
FUNC(void, OS_CODE) osek_interrupt_restore(void)
{
	VAR(CoreIdType, AUTOMATIC) core_id = GetCoreID();

	if (accurate_read(&osek_os_interrupt_nesting[core_id]) > 0) {
		accurate_dec(&osek_os_interrupt_nesting[core_id]);
		local_irq_restore(osek_os_interrupt_flags[core_id]);
	}

	if (irqs_disabled()) {
		enable_irq();
	}
}
